﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace MyLib.Web.Pages {
	/// <summary>
	/// 
	/// </summary>
	public abstract class LoginPage : MyPage {
		/// <summary>
		/// 
		/// </summary>
		protected virtual double DefaultTicketExpiration {
			get {
				return 2D; // two hours.
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="e"></param>
		protected override void OnLoad(EventArgs e) {
			base.OnLoad(e);

			Title = Properties.Main.TitleLogin;

			// Hide "Failure" control is exists.
			var failureCtrl = Form.FindDescendantControl<Control>("FailureText") as ITextControl;
			failureCtrl.Text = string.Empty;
			((Control)failureCtrl).Visible = false;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="source"></param>
		/// <param name="args"></param>
		/// <returns></returns>
		protected override bool OnBubbleEvent(object source, EventArgs args) {
			bool causesValidation = false;
			string validationGroup = string.Empty;

			if (args is CommandEventArgs) {
				IButtonControl commandSource = source as IButtonControl;
				if (commandSource != null) {
					causesValidation = commandSource.CausesValidation;
					validationGroup = commandSource.ValidationGroup;
				}

				var cargs = (CommandEventArgs)args;

				if (string.Equals(cargs.CommandName, "Login", StringComparison.OrdinalIgnoreCase)) {
					HandleLogin(causesValidation, validationGroup);
					return true;
				} else {
					return false;
				}
			} else {
				return false;
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="causesValidation"></param>
		/// <param name="validationGroup"></param>
		private void HandleLogin(bool causesValidation, string validationGroup) {
			if (!causesValidation || this.IsValid) {
				var userName = string.Empty; // Default value.
				var password = string.Empty; // Default value.
				var isPersistent = false; // Default value.

				var userNameCtrl = Form.FindDescendantControl<Control>("UserName") as ITextControl;
				var passwordCtrl = Form.FindDescendantControl<Control>("Password") as ITextControl;
				var isPersistentCtrl = Form.FindDescendantControl<Control>("RememberMe") as ICheckBoxControl;

				var failureCtrl = Form.FindDescendantControl<Control>("FailureText") as ITextControl;

				if (userNameCtrl != null) {
					userName = userNameCtrl.Text.Trim();
				}
				if (passwordCtrl != null) {
					password = passwordCtrl.Text.Trim();
				}
				if (isPersistentCtrl != null) {
					isPersistent = isPersistentCtrl.Checked;
				}

				var vr = ValidateUserPassword(userName, password);
				if (vr != null) {
					SetAuth(vr.Item1, vr.Item2, vr.Item3, vr.Item4, isPersistent, vr.Item5);
				} else if (failureCtrl != null) {
					// Show "Failure" control if exists.
					failureCtrl.Text = ErrorInvalidUsernameOrPasswordText;
					((Control)failureCtrl).Visible = true;
				}
			} // end of if.
		}

		/// <summary>
		/// Validate username and password.
		/// </summary>
		/// <param name="userName"></param>
		/// <param name="password"></param>
		/// <returns></returns>
		protected virtual Tuple<string, string, string, string, IEnumerable<Tuple<int, string>>> ValidateUserPassword(string userName, string password) {
			var ap = GetDbProvider<MyLib.Data.AuthorityProvider>();

			var ut = ap.Validate(userName, password);

			if (ut.Count == 0) {
				return null;
			} else {
				var userId = ut[0].UserId;
				var er = ap.FindUserExtByUserId(userId);
				return new Tuple<string, string, string, string, IEnumerable<Tuple<int, string>>>(
					ut[0].UserId,
					ut[0].UserName,
					ut[0].IsRealNameNull ? "" : ut[0].RealName,
					ut[0].IsGroupIdNull ? "" : ut[0].GroupId,
					er.Select(
						row => new Tuple<int, string>(row.ExtId, row.ExtValue)
						)
				);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		protected virtual string ErrorInvalidUsernameOrPasswordText {
			get {
				return Properties.Main.ErrorInvalidUsernameOrPassword;
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="userId"></param>
		/// <param name="userName"></param>
		/// <param name="realName"></param>
		/// <param name="groupId"></param>
		/// <param name="isPersistent"></param>
		/// <param name="ext"></param>
		protected virtual void SetAuth(string userId, string userName, string realName, string groupId, bool isPersistent, IEnumerable<Tuple<int, string>> ext) {
			double ticketExpiration;

			var sp = GetDbProvider<MyLib.Data.SettingsProvider>();

			if (!double.TryParse(sp.LoadByKey(2, "ticketExpiration"), out ticketExpiration)) {
				ticketExpiration = DefaultTicketExpiration;
			}

			var issureDate = DateTime.Now;
			var expirationDate = issureDate.AddHours(ticketExpiration);
			FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
				1,
				userId,
				issureDate,
				expirationDate,
				isPersistent,
				string.Empty,
				FormsAuthentication.FormsCookiePath);
			var encryptedTicket = FormsAuthentication.Encrypt(ticket);
			var ticketCookie = new HttpCookie(
				FormsAuthentication.FormsCookieName, encryptedTicket);
			ticketCookie.Domain = FormsAuthentication.CookieDomain;
			ticketCookie.Path = FormsAuthentication.FormsCookiePath;
			if (isPersistent) {
				ticketCookie.Expires = DateTime.MaxValue;
			}
			Response.Cookies.Add(ticketCookie);

			var loginNameCookie = new HttpCookie(HttpContextExtensions.CookieLoginName,
				HttpUtility.UrlEncode(userName, System.Text.Encoding.UTF8));
			loginNameCookie.Domain = FormsAuthentication.CookieDomain;
			loginNameCookie.Path = FormsAuthentication.FormsCookiePath;
			Response.Cookies.Add(loginNameCookie);

			var realNameCookie = new HttpCookie(HttpContextExtensions.CookieRealName,
				HttpUtility.UrlEncode(realName, System.Text.Encoding.UTF8));
			realNameCookie.Domain = FormsAuthentication.CookieDomain;
			realNameCookie.Path = FormsAuthentication.FormsCookiePath;
			Response.Cookies.Add(realNameCookie);

			var groupIdCookie = new HttpCookie(HttpContextExtensions.CookieGroupId,
				HttpUtility.UrlEncode(groupId, System.Text.Encoding.UTF8));
			realNameCookie.Domain = FormsAuthentication.CookieDomain;
			realNameCookie.Path = FormsAuthentication.FormsCookiePath;
			Response.Cookies.Add(groupIdCookie);

			if (ext != null) {
				// Create cookies for ext.
				foreach (var entry in ext) {
					var cookie = new HttpCookie(HttpContextExtensions.CookieExtPrefix + entry.Item1,
						HttpUtility.UrlEncode(entry.Item2, System.Text.Encoding.UTF8));
					cookie.Domain = FormsAuthentication.CookieDomain;
					cookie.Path = FormsAuthentication.FormsCookiePath;
					Response.Cookies.Add(cookie);
				}
			}

			// Redirect back to original URL.
			Response.Redirect(FormsAuthentication.GetRedirectUrl(userId, isPersistent));
		}
	} // end of LoginPage.
}
